﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using InductBase;
using System.Xml;
using SharpBolt.Basics;
using SharpBolt.Graphics;
using Newtonsoft.Json;

namespace SharpBolt.Graphics.GraphicSources
{
    [JsonObject]
	public class RasterFontSourse : IGraphicSource
	{
		string name;
		public string Name
		{
			get { return name; }
			set { name = value; }
		}

		int bw_chars;

		public int CharsInterval
		{
			get { return bw_chars; }
			set { bw_chars = value; }
		}

		int bw_rows;
		public int RowsInterval
		{
			get { return bw_rows; }
			set { bw_rows = value; }
		}

		int bw_space;
		public int SpaceInterval
		{
			get { return bw_space; }
			set { bw_space = value; }
		}

		SbBitmap bmp;
		public SbBitmap Bitmap
		{
			get { return bmp; }
			set { bmp = value; }
		}

        public Dictionary<char, Gliph> gliphs { get; set; }

		public static RasterFontSourse LoadIndieFont(string name)
		{
			RasterFontSourse result = new GraphicSources.RasterFontSourse();
			result.Name = name;
			result.bw_chars = 0;
			result.bw_space = 5;

			result.bmp = Engine.GraphicRender.GetBitmap(name + ".png");

			result.gliphs = new Dictionary<char, Gliph>();

			XmlDocument doc = new XmlDocument();
			doc.Load(name + ".xml");

			//===========
			XmlNode node = doc.DocumentElement;//font
			int numchar = Int32.Parse(node.Attributes["num_characters"].Value);

			var XChars = node.ChildNodes;//chars

			for (int i = 0; i < XChars.Count; i++)
			{
				Gliph g = new Gliph();
				g.area = new Rec();

				int charval = Int32.Parse(XChars[i].Attributes["id"].Value);
				g.ch = (char)charval;

				int x = Int32.Parse(XChars[i].Attributes["x"].Value);
				int y = Int32.Parse(XChars[i].Attributes["y"].Value);
				int width = Int32.Parse(XChars[i].Attributes["width"].Value);
				int height = Int32.Parse(XChars[i].Attributes["height"].Value);

				g.area.Min = new Vec2(x, y);
				g.area.Size = new Vec2(width, height);

				if (result.bw_rows < height) result.bw_rows = height;

				result.gliphs[g.ch] = g;
			}

			return result;
		}

		public static RasterFontSourse LoadSharpBoltFont(string name)
		{
			RasterFontSourse result = new GraphicSources.RasterFontSourse();
			result.Name = name;
			result.bw_chars = 0;
			result.bw_space = 5;

			result.bmp = Engine.GraphicRender.GetBitmap(name + ".png");

			result.gliphs = new Dictionary<char, Gliph>();

			string[] lines = Engine.Miscs.LoadText(name + ".sbf").Split(new[]{'\n'}, StringSplitOptions.RemoveEmptyEntries);


			int numchar = Int32.Parse(lines[0]);
			result.bw_chars = -Int32.Parse(lines[1]);

			for (int i = 0; i < numchar; i++)
			{
				string[] args = lines[i+2].Split(new[] { '\t' }, StringSplitOptions.RemoveEmptyEntries);

				Gliph g = new Gliph();
				g.area = new Rec();

				g.ch = args[0][0];

				int x = Int32.Parse(args[1]);
				int y = Int32.Parse(args[2]);
				int width = Int32.Parse(args[3]);
				int height = Int32.Parse(args[4]);

				g.area.Min = new Vec2(x, y);
				g.area.Size = new Vec2(width, height);

				if (result.bw_rows < height) result.bw_rows = height;

				result.gliphs[g.ch] = g;
			}

			return result;
		}

		public void Draw(Painter painter, Transformation transformation, Color color)
		{
			Draw(painter, transformation, "ABC", color);
		}

		public void Draw(Painter painter, Transformation transformation, string text, Color color)
		{
			Draw(painter, transformation.Position.X, transformation.Position.Y, text, color,transformation.Scale, transformation.Rotation);
		}

		public void Draw(Painter painter, Vec2 position, string text, Color color, Vec2 scale, double angle = 0)
		{
			Draw(painter, position.X, position.Y, text, color, scale, angle);
		}

		public void Draw(Painter painter, double x, double y, string text, Color color, Vec2 scale, double angle = 0)
		{
			Draw(painter, x, y, -1, -1, 0, text, color, scale, angle);
		}

		public void Draw(Painter painter, Vec2 position, string text, Action<RasterFontGliphArgs> gliphRender)
		{
			Draw(painter, position.X, position.Y, -1, -1, 0, text, Colors.White, Vec2.Unit, 0, gliphRender);
		}

		public void Draw(Painter painter, double x, double y, double maxwidth, double maxheight, double rowShift, string text, Color color, Vec2 scale, double angle = 0, Action<RasterFontGliphArgs> gliphRender = null)
		{
			if (maxheight == 0) return;
			if (maxwidth == 0) return;
		
			Vec2 startPoint = new Vec2(x, y);

			double rad = Math.PI * 2 * angle / 360.0;
			double cos = Math.Cos(rad);
			double sin = Math.Sin(rad);

			int cx, cy;
			cx = 0;
			cy = 0;

			for (int i = 0; i < text.Length; i++)
			{
				if (text[i] == '\n') { cy += bw_rows; cx = 0; continue; }
				if (text[i] == ' ') { cx += bw_space; continue; }

				Gliph gl;

				if(!gliphs.ContainsKey(text[i])) continue;
				gl = gliphs[text[i]];


				if (cx < 0) continue;
				if (cy - rowShift < 0) continue;

				if (maxwidth > 0)
				{
					if ((cx + gl.area.Size.X) * scale.X > maxwidth)
						continue;
				}

				if (maxheight > 0)
				{
					if ((cy + gl.area.Size.Y - rowShift) * scale.Y > maxheight)
						continue;
				}

				if (gliphRender != null)
				{
					RasterFontGliphArgs args = new RasterFontGliphArgs()
					{
						Gliph = gl,
						Index = i,
						MaxHeight = maxheight,
						MaxWidth = maxwidth,
						RowShift = rowShift,
						Painter = painter,
						ShiftX = cx,
						ShiftY = cy,
						Source = this,
						StartPoint = startPoint,
						Text = text,
						Scale = scale,
						Color = color,
						Angle = angle
					};

					gliphRender(args);
				}
				else
				{
					DefaultGliphDraw(painter, rowShift, ref color, ref scale, angle, ref startPoint, cx, cy, gl);
				}

				cx += (int)gl.area.Size.X + bw_chars;
			}
		}

		public void DefaultGliphDrawByArgs(RasterFontGliphArgs args)
		{
			args.Painter.DrawBitmap(
				Bitmap,
				new Transformation(
				GetGliphPosition(args),
				args.Scale,
				Vec2.Null,
				args.Angle),
				args.Color,
				args.Gliph.area
				);
		}

		public Vec2 GetGliphPosition(RasterFontGliphArgs args)
		{
			return args.StartPoint + ((new Vec2(args.ShiftX, args.ShiftY - args.RowShift)) * args.Scale).Rotate(args.Angle);
		}

		public void DefaultGliphDraw(Painter painter, double rowShift, ref Color color, ref Vec2 scale, double angle, ref Vec2 startPoint, int cx, int cy, Gliph gl)
		{
			painter.DrawBitmap(
				Bitmap,
				new Transformation(
				//new Vec2(
				//    x + cx * cos - cy * sin,
				//    y + cx * sin + cy * cos),
				startPoint + ((new Vec2(cx, cy - rowShift)) * scale).Rotate(angle),
				scale,
				Vec2.Null,
				angle),
				color,
				gl.area
				);
		}

		public void DrawInRec(Painter painter, Rec area, string text, Color color, Vec2 scale, bool centred = false, double angle = 0, Action<RasterFontGliphArgs> gliphRender = null)
		{
            string stext = SplitByWords(text, area);
			Rec sarea = area.Copy();

			if (centred)
			{
				Vec2 size = GetSize(stext);
				sarea.Location += (sarea.Size - size) / 2;
			}

			Draw(painter, sarea.X, sarea.Y, -1, sarea.Height, 0, stext, color, scale, angle, gliphRender);
		}

		public Vec2 GetSize(string text)
		{
			int cx, cy, rx;
			cx = 0;
			cy = 0;
			rx = 0;

			for (int i = 0; i < text.Length; i++)
			{
				if (text[i] == '\n') { cy += bw_rows; cx = 0; continue; }
				if (text[i] == ' ') { cx += bw_space; continue; }

				Gliph gl;

				if(!gliphs.ContainsKey(text[i])) continue;
				gl = gliphs[text[i]];


				cx += (int)gl.area.Width + bw_chars;
				if (cx > rx) rx = cx;
			}

			cy += bw_rows;
			return new Vec2(rx, cy);
		}
        
        public string SplitByWords(string text, Rec area)
		{
			return SplitByWords(area, text, Vec2.Unit);
		}

		public string SplitByWords(Rec area, string text, Vec2 scale)
		{
			if (scale == Vec2.Null) scale = Vec2.Unit;
			area = Rec.FromLocationSize(area.Location, area.Size / scale);
			var words = text.Split(' ');
			string result = words[0];

			for (int i = 1; i < words.Length; i++)
			{ 
				double width = GetSize(result + ' ' + words[i]).X;

				if (width < area.Width)
					result += ' ' + words[i];
				else
					result += '\n' + words[i];
			}

			return result;
		}

		private Vec2 _size;
		public InductBase.Vec2 Size
		{
			get
			{
				return _size;
			}

			set
			{
				_size = value;
			}
		}
	}

    [JsonObject]
	public class Gliph
	{
		public char ch;
		public Rec area;
	};

    public class RasterFontGliphArgs : System.EventArgs
    {
        public Painter Painter { get; set; }

        public RasterFontSourse Source { get; set; }
        public string Text { get; set; }
        public Gliph Gliph { get; set; }
        public int Index { get; set; }

        public Vec2 StartPoint { get; set; }
        public int ShiftX { get; set; }
        public int ShiftY { get; set; }

        public double MaxWidth { get; set; }
        public double MaxHeight { get; set; }
        public double RowShift { get; set; }

        public Color Color { get; set; }
        public Vec2 Scale { get; set; }

        public double Angle { get; set; }
    }
}